﻿using System;
using System.Collections.Generic;
using System.Net;
using System.Text;
using Newtonsoft.Json;

namespace BrightcoveAPI
{
    // <summary>
    // .NET typed implementation of the Brightcove Media APIs, providing read/write access 
    // to the videos in a publisher's media library using HTTP calls. 
    //
    // The Brightcove Media Library API, or Media API for short, is a REST-based API for 
    // accessing the content and metadata in your Brightcove account. The Media API gives 
    // you read and write access to the videos and playlists in your Brightcove account.
    // To prevent unauthorized access to the metadata in your account, access to the API is 
    // protected with tokens that you pass as a parameter when making API calls. Like other 
    // web-based APIs, tokens are generated for you by Brightcove and protected by you.
    //
    // To use the Media API, you need to request a token from Brightcove Customer Support.
    // 
    // http://help.brightcove.com/request-support.cfm
    //
    // NOTE: The Media Write API is not available for Brightcove Basic customers. 
    // </summary>    
    public static class BCAPI
    {
        #region Configuration

        public static BCAPIConfig Configuration = new BCAPIConfig();

        #endregion Configuration

        #region Media Read API

        #region FindAllVideos
        // <summary>
        // Find all videos in the Brightcove media library for this account.
        // </summary>        
        public static Dictionary<long, BCVideo> FindAllVideos(int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String>fields, Boolean getItemCount)
        {
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_all_videos");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            reqparams.Add("sort_order", sortOrder.ToString());
            reqparams.Add("sort_by", sortBy.ToString());
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        public static Dictionary<long, BCVideo> FindAllVideos(int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields)
        {
            return FindAllVideos(pageSize, pageNumber, sortBy, sortOrder, fields, true);
        }

        public static Dictionary<long, BCVideo> FindAllVideos(int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder)
        {
            return FindAllVideos(pageSize, pageNumber, sortBy, sortOrder, null, true);
        }

        public static Dictionary<long, BCVideo> FindAllVideos(int pageSize, int pageNumber, BCSortByType sortBy)
        {
            return FindAllVideos(pageSize, pageNumber, sortBy, BCSortOrderType.ASC, null, true);
        }

        public static Dictionary<long, BCVideo> FindAllVideos(int pageSize)
        {
            return FindAllVideos(pageSize, -1, BCSortByType.CREATION_DATE, BCSortOrderType.ASC, null, true);
        }

        public static Dictionary<long, BCVideo> FindAllVideos()
        {
            return FindAllVideos(-1, -1, BCSortByType.CREATION_DATE, BCSortOrderType.ASC, null, true);
        }

        #endregion FindAllVideos

        #region FindVideoById
        // <summary>
        // Finds a single video with the specified id.
        // </summary>        
        public static BCVideo FindVideoById(long videoId, List<String> fields)
        {
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_video_by_id");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("video_id", videoId.ToString());
            if (fields != null) reqparams.Add("fields", Implode(fields));

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a single video
            return GetSingleVideo(reader);
        }

        // <summary>
        // Finds a single video with the specified id.
        // </summary>  
        public static BCVideo FindVideoById(long videoId)
        {
            return FindVideoById(videoId, null);
        }

        #endregion FindVideoById

        #region FindRelatedVideos

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindRelatedVideos(long videoId, int pageSize, int pageNumber, List<String> fields, Boolean getItemCount)
        {
            List<BCVideo> videos = new List<BCVideo>();
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_related_videos");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("video_id", videoId.ToString());
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        public static Dictionary<long, BCVideo> FindRelatedVideos(long videoId)
        {
            return FindRelatedVideos(videoId, -1, -1, null, false);
        }

        public static Dictionary<long, BCVideo> FindRelatedVideos(long videoId, int pageSize, int pageNumber)
        {
            return FindRelatedVideos(videoId, pageSize, pageNumber, null, false);
        }

        public static Dictionary<long, BCVideo> FindRelatedVideos(long videoId, int pageSize, int pageNumber, List<String> fields)
        {
            return FindRelatedVideos(videoId, pageSize, pageNumber, fields, false);
        }

        #endregion FindRelatedVideos

        #region FindVideosByIds

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByIds(List<long> videoIds, List<String> fields)
        {
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_ids");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("video_ids", Implode(videoIds));
            if (fields != null) reqparams.Add("fields", Implode(fields));

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        public static Dictionary<long, BCVideo> FindVideosByIds(List<long> videoIds)
        {
            return FindVideosByIds(videoIds, null);
        }

        #endregion FindVideosByIds

        #region FindVideoByReferenceId

        // <summary>
        // 
        // </summary>        
        public static BCVideo FindVideoByReferenceId(String referenceId, List<String> fields)
        {
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_video_by_reference_id");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("reference_id", referenceId);
            if (fields != null) reqparams.Add("fields", Implode(fields));

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a single video
            return GetSingleVideo(reader);
        }

        public static BCVideo FindVideoByReferenceId(String referenceId)
        {
            return FindVideoByReferenceId(referenceId, null);
        }

        #endregion FindVideoByReferenceId

        #region FindVideosByReferenceIds

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByReferenceIds(List<String> referenceIds, List<String> fields)
        {
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_reference_ids");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("reference_ids", Implode(referenceIds));
            if (fields != null) reqparams.Add("fields", Implode(fields));

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        public static Dictionary<long, BCVideo> FindVideosByReferenceIds(List<String> referenceIds)
        {
            return FindVideosByReferenceIds(referenceIds, null);
        }

        #endregion FindVideosByReferenceIds
        
        #region FindVideosByUserId

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByUserId(String userId, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields, Boolean getItemCount)
        {
            List<BCVideo> videos = new List<BCVideo>();
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_user_id");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("user_id", userId);
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            reqparams.Add("sort_order", sortOrder.ToString());
            reqparams.Add("sort_by", sortBy.ToString());
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        #endregion FindVideosByUserId

        #region FindVideosByCampaignId

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByCampaignId(long campaignId, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields, Boolean getItemCount)
        {
            List<BCVideo> videos = new List<BCVideo>();
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_campaign_id");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("campaign_id", campaignId.ToString());
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            reqparams.Add("sort_order", sortOrder.ToString());
            reqparams.Add("sort_by", sortBy.ToString());
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        #endregion FindVideosByCampaignId

        #region FindVideosByText

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByText(String text, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields, Boolean getItemCount)
        {
            List<BCVideo> videos = new List<BCVideo>();
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_text");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("text", text);
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            reqparams.Add("sort_order", sortOrder.ToString());
            reqparams.Add("sort_by", sortBy.ToString());
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        #endregion FindVideosByText

        #region FindVideosByTags

        // <summary>
        // 
        // </summary>        
        public static Dictionary<long, BCVideo> FindVideosByTags(String and_tags, String or_tags, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields, Boolean getItemCount)
        {
            List<BCVideo> videos = new List<BCVideo>();
            BCAPIRequest req = new BCAPIRequest();
            Dictionary<String, String> reqparams = new Dictionary<string, string>();

            //Build the REST parameter list
            reqparams.Add("command", "find_videos_by_tags");
            reqparams.Add("token", BCAPI.Configuration.ReadToken);
            reqparams.Add("and_tags", and_tags);
            reqparams.Add("or_tags", or_tags);
            reqparams.Add("get_item_count", getItemCount.ToString().ToLower());
            if (fields != null) reqparams.Add("fields", Implode(fields));
            reqparams.Add("sort_order", sortOrder.ToString());
            reqparams.Add("sort_by", sortBy.ToString());
            if (pageNumber >= 0) reqparams.Add("page_number", pageNumber.ToString());
            if (pageSize >= 0) reqparams.Add("page_size", pageSize.ToString());

            //Get the JSon reader returned from the APIRequest
            JsonReader reader = req.Execute(reqparams);

            //Return a list of videos
            return GetMultipleVideos(reader);
        }

        public static Dictionary<long, BCVideo> FindVideosByTags(String and_tags, String or_tags)
        {
            return FindVideosByTags(and_tags, or_tags, -1, -1, BCSortByType.CREATION_DATE, BCSortOrderType.ASC, null, false);
        }

        public static Dictionary<long, BCVideo> FindVideosByTags(String and_tags, String or_tags, int pageSize, int pageNumber)
        {
            return FindVideosByTags(and_tags, or_tags, pageSize, pageNumber, BCSortByType.CREATION_DATE, BCSortOrderType.ASC, null, false);
        }

        public static Dictionary<long, BCVideo> FindVideosByTags(String and_tags, String or_tags, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder)
        {
            return FindVideosByTags(and_tags, or_tags, pageSize, pageNumber, sortBy, sortOrder, null, false);
        }

        public static Dictionary<long, BCVideo> FindVideosByTags(String and_tags, String or_tags, int pageSize, int pageNumber, BCSortByType sortBy, BCSortOrderType sortOrder, List<String> fields)
        {
            return FindVideosByTags(and_tags, or_tags, pageSize, pageNumber, sortBy, sortOrder, fields, false);
        }

        #endregion FindVideosByTags     
        
        #region Private Read Methods
        // <summary>
        // Get a Dictionary of videos from the JSON string, or return an empty Dictionary if none found
        // </summary>        
        private static Dictionary<long, BCVideo> GetMultipleVideos(JsonReader reader)
        {
            Dictionary<long, BCVideo> videos = new Dictionary<long, BCVideo>();           

            //Get the first start object
            reader.Read();
            if (reader.TokenType == JsonToken.StartObject)
            {
                //Check if this is an array of videos
                reader.Read();
                if (reader.TokenType == JsonToken.PropertyName)
                {
                    if (reader.Value.ToString() == "items")
                    {
                        reader.Read();
                        if (reader.TokenType == JsonToken.StartArray)
                        {
                            //Parse all video objects within the array
                            reader.Read();
                            while (reader.TokenType == JsonToken.StartObject)
                            {
                                BCVideo video = new BCVideo(reader);
                                videos.Add(video.id,video);
                            }
                        }
                    }
                    else if (reader.Value.ToString() == "error")
                    {
                        reader.Read();
                        throw new Exception("BCAPI Read exception: " + reader.Value.ToString());
                    }
                }
                else
                {
                    throw new Exception("BCAPI Read exception: Unexpected JSON format within string");
                }
            }
            else
            {
                //Check if returned JSON string is empty
                if (reader.Value == null)
                {
                    //The request did not return any items
                }
                else
                {
                    //The request returned an unexpected string
                    throw new Exception("BCAPI Read exception: Unexpected JSON format at beginning of string");
                }
            }
            return videos;
        }


        // <summary>
        // Get a single video from the JSON string, return null if no video element is found
        // </summary>        
        private static BCVideo GetSingleVideo(JsonReader reader)
        {
            BCVideo video = null;
            reader.Read();
            if (reader.TokenType == JsonToken.StartObject)
            {
                video = new BCVideo(reader);
            }
            else
            {
                //Check if returned JSON string is empty
                if (reader.Value == null)
                {
                    //The request did not return any items
                }
                else
                {
                    //The request returned an unexpected string
                    throw new Exception("BCAPI Read exception: Unexpected JSON format at beginning of string");
                }
            }
            return video;
        }

        #endregion Private Read Methods

        #endregion Media Read API

        #region Private Utility Methods
        // <summary>
        // 
        // </summary>        
        private static String Implode(List<String> values)
        {
            String result = "";
            int i = 1;
            foreach (String s in values)
            {
                result = result + s;
                if (i < values.Count)
                {
                    result = result + ",";
                }
                i++;
            }
            return result;
        }

        // <summary>
        // 
        // </summary>        
        private static String Implode(List<long> values)
        {
            String result = "";
            int i = 1;
            foreach (long l in values)
            {
                result = result + l.ToString();
                if (i < values.Count)
                {
                    result = result + ",";
                }
            }
            return result;
        }

        // <summary>
        // 
        // </summary> 
        private static List<String> ExplodeString(String values)
        {
            List<String> result = new List<string>();
            String[] vsplit = values.Split(',');
            foreach (String s in vsplit)
            {
                result.Add(s.Trim());
            }
            return result;
        }

        // <summary>
        // 
        // </summary> 
        private static List<long> ExplodeLong(String values)
        {
            List<long> result = new List<long>();
            String[] vsplit = values.Split(',');
            foreach (String s in vsplit)
            {
                result.Add(long.Parse(s));
            }
            return result;
        }

        #endregion Private Utility Methods
    }
}
